home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / VMSify / VMSify.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  17.4 KB  |  593 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        VMSify.c
  3.  
  4.     Contains:    Stream module that does nothing.
  5.  
  6.     Written by:    Quinn "The Eskimo!"
  7.  
  8.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19. */
  20.  
  21. /*
  22.     A Note About States
  23.     -------------------
  24.     Throughout the source, you'll see comments like:
  25.     
  26.         sta_X + Y -> sta_Z
  27.     
  28.     These comments describe state transitions in the TPI state machine,
  29.     which is documented in Figure 9 of the TPI specification:
  30.     
  31.         <http://developer.apple.com/macos/opentransport/docs/dev/tpi.pdf>
  32.     
  33.     The basic idea is that, when you start in state X and get event Y,
  34.     you transition to state Z.
  35.     
  36.     This module has to track the state of the provider to ensure that it
  37.     only turns on the VMSify'ing functionality when the provider is being
  38.     used for outgoing streams connected to the telnet port.
  39. */
  40.  
  41. /////////////////////////////////////////////////////////////////////
  42. // The OT debugging macros in <OTDebug.h> require this variable to
  43. // be set.
  44.  
  45. #ifndef qDebug
  46. #define qDebug    0
  47. #endif
  48.  
  49. /////////////////////////////////////////////////////////////////////
  50. // Pick up all the standard OT module stuff.
  51.  
  52. #include <OpenTransportKernel.h>
  53. #include <OpenTransportProviders.h>
  54.  
  55. /////////////////////////////////////////////////////////////////////
  56. // OpenTptMiscUtilsPPC.o is not exporting OTDebugStr currently
  57. // (Universal Interfaces 3.3.1).  So we just define our own version
  58. // here.
  59.  
  60. extern pascal void OTDebugStr(const char *msg)
  61. {
  62.     debugstr(msg);
  63. }
  64.  
  65. /////////////////////////////////////////////////////////////////////
  66. // Some simple routines we use in our various assertions.
  67.  
  68. static Boolean IsReadQ(queue_t* q)
  69.     // Returns true if q is the read queue of a queue pair.
  70. {
  71.     return ((q->q_flag & QREADR) != 0);
  72. }
  73.  
  74. static Boolean IsWriteQ(queue_t* q)
  75.     // Returns true if q is the write queue of a queue pair.
  76. {
  77.     return ((q->q_flag & QREADR) == 0);
  78. }
  79.  
  80. /////////////////////////////////////////////////////////////////////
  81. // Per-Stream information
  82.  
  83. enum {
  84.     kTelnetPort = 23
  85. };
  86.  
  87. // This structure is used to hold the per-stream data for the module.
  88. // While module's can use normal global variables to store real globals,
  89. // they must maintain their own per-stream data structures.  I use
  90. // mi_open_comm to allocate this data structure when the stream is
  91. // opened.  mi_open_comm stores the address of this data structure in the
  92. // read and write queue's q_ptr field, so the rest of the code
  93. // can get to it by calling the GetPerStreamData function.
  94.  
  95. enum {
  96.     kVMSifyPerStreamDataMagic = 'VMS!'
  97. };
  98.  
  99. struct PerStreamData
  100. {
  101.     OSType                 magic;                // kVMSifyPerStreamDataMagic = 'VMS!' for debugging
  102.     // Your per-stream data structures go here.
  103.     InetPort            lastConnReqPort;    // outgoing connection requests setup this
  104.     Boolean                vmsify;                // if we made a successful outgoing connection to port kTelnetPort, this is true
  105. };
  106. typedef struct PerStreamData PerStreamData, *PerStreamDataPtr;
  107.  
  108. static PerStreamDataPtr GetPerStreamData(queue_t* readOrWriteQ)
  109.     // You can pass both the read or the write queue to this routine
  110.     // because mi_open_comm sets up both q_ptr's to point to the
  111.     // queue local data.
  112.     //
  113.     // Note that, in order to avoid the overhead of a function call,
  114.     // you would normally use inline code (or a macro)
  115.     // to get your per-stream data instead of using a separate function.
  116.     // However I think the separate function makes things clearer.
  117.     // I also acts as a central bottleneck for my debugging code.
  118.     //
  119.     // Environment: any standard STREAMS entry point
  120. {
  121.     PerStreamDataPtr streamData;
  122.     
  123.     streamData = (PerStreamDataPtr) readOrWriteQ->q_ptr;
  124.  
  125.     OTAssert("GetPerStreamData: what streamData", streamData != nil);
  126.     OTAssert("GetPerStreamData: Bad magic", streamData->magic == kVMSifyPerStreamDataMagic);
  127.     
  128.     return streamData;
  129. }
  130.  
  131. // mi_open_comm and mi_close_comm (and also mi_detach and mi_close_detached)
  132. // use this global to store the list of open streams to this module.
  133.  
  134. static char* gStreamList = nil;
  135.  
  136. /////////////////////////////////////////////////////////////////////
  137. // Open routine
  138.  
  139. static SInt32 VMSifyOpen(queue_t* rdq, dev_t* dev, SInt32 flag, SInt32 sflag, cred_t* creds)
  140.     // This routine is called by STREAMS when a new stream is connected to
  141.     // our module.  The bulk of the work here is done by the Mentat helper
  142.     // routine mi_open_comm.
  143.     //
  144.     // Environment: standard STREAMS entry point
  145. {
  146.     SInt32 err;
  147.     PerStreamDataPtr streamData;
  148.  
  149.     OTAssert("VMSifyOpen: Not the read queue", IsReadQ(rdq) );
  150.  
  151.     // OTDebugBreak("VMSifyOpen");
  152.     
  153.     err = noErr;
  154.     
  155.     // If we already have per-stream data for this stream, the stream is being reopened.
  156.     // In that case, we can just return.
  157.     // Note that we can't call GetPerStreamData because it checks that streamData is not nil.
  158.     
  159.     if ( rdq->q_ptr != nil ) {
  160.         goto done;
  161.     }
  162.  
  163.     // Make sure we're being opened properly -- because we're a module we
  164.     // require a "module" open.  Other possibilities are the value 0 (used
  165.     // to open a specific minor device number (ie stream) on a device driver),
  166.     // and CLONEOPEN (used to open a new stream to a device number where you
  167.     // don't care what device number you get -- the typical behaviour for
  168.     // networking (as opposed to serial) devices).
  169.     
  170.     if ( (err == noErr) && (sflag != MODOPEN) ) {
  171.         err = ENXIO;
  172.     }
  173.     
  174.     // Use the mi_open_comm routine to allocate our per-stream data.  Then
  175.     // zero out the entire per-stream data record and fill out the fields
  176.     // we're going to need.
  177.     
  178.     if (err == noErr) {
  179.         err = mi_open_comm(&gStreamList, sizeof(PerStreamData), rdq, dev, flag, sflag, creds);
  180.         if ( err == noErr ) {
  181.             // Note that we can't call GetPerStreamData because the magic is not set up yet.
  182.             streamData = (PerStreamDataPtr) rdq->q_ptr;
  183.             
  184.             OTMemzero(streamData, sizeof(PerStreamData));
  185.             
  186.             streamData->magic = kVMSifyPerStreamDataMagic;
  187.             streamData->lastConnReqPort = 0;
  188.             streamData->vmsify = false;
  189.         }
  190.     }
  191.  
  192. done:
  193.     return err;
  194. }
  195.  
  196. /////////////////////////////////////////////////////////////////////
  197. // Close routine
  198.  
  199. static SInt32 VMSifyClose(queue_t* rdq, SInt32 flags, cred_t* credP)
  200.     // This routine is called by STREAMS when a stream is being
  201.     // disconnected from our module (ie closed).  The bulk of the work
  202.     // is done by the magic Mentat helper routine mi_close_comm.
  203.     //
  204.     // Environment: standard STREAMS entry point
  205. {
  206.     #pragma unused(flags)
  207.     #pragma unused(credP)
  208.  
  209.     OTAssert("VMSifyClose: Not the read queue", IsReadQ(rdq) );
  210.  
  211.     (void) mi_close_comm(&gStreamList, rdq);
  212.  
  213.     return 0;
  214. }
  215.  
  216. /////////////////////////////////////////////////////////////////////
  217.  
  218. enum {
  219.     kNoPrimitive = -1
  220. };
  221.  
  222. static long GetPrimitive(mblk_t* mp)
  223.     // GetPrimitive gets the TPI/DLPI primitive out of a message block.
  224.     // It returns kNoPrimitive if the message block is of the wrong
  225.     // type or there is no primitive.
  226.     //
  227.     // Environment: any standard STREAMS entry point
  228. {
  229.     if ((mp->b_datap->db_type == M_PROTO || mp->b_datap->db_type == M_PCPROTO) && MBLK_SIZE(mp) >= sizeof(long) ) {
  230.         return ( (union T_primitives*) mp->b_rptr)->type;
  231.     } else {
  232.         return kNoPrimitive;
  233.     }
  234. }
  235.  
  236. /////////////////////////////////////////////////////////////////////
  237. // Write-side put routine
  238.  
  239. static InetPort ExtractPortFromDNSAddress(DNSAddress *dnsAddr, ByteCount DEST_length)
  240.     // Parse the port number out of a DNSAddress structure.
  241.     // Yetch!
  242. {
  243.     InetPort portNum;
  244.     char *cursor;
  245.     char *limit;
  246.     
  247.     portNum = 0;
  248.     cursor = dnsAddr->fName;
  249.     limit = ((char *) dnsAddr) + DEST_length;
  250.     
  251.     // Skip past all the stuff before the colon.
  252.     
  253.     while ( cursor < limit && *cursor != ':' ) {
  254.         cursor += 1;
  255.     }
  256.     
  257.     // If there's a colon, skip over it and then
  258.     // extract digits from there.
  259.     
  260.     if (*cursor == ':') {
  261.         cursor += 1;                // skip past ':'
  262.         while (cursor < limit && *cursor >= '0' && *cursor <= '9') {
  263.             portNum = (portNum * 10) + (*cursor - '0');
  264.             cursor += 1;
  265.         }
  266.     }
  267.     return portNum;
  268. }
  269.  
  270. static SInt32 VMSifyWritePut(queue_t* q, mblk_t* mp)
  271.     // This routine is called by STREAMS when it has a message for our
  272.     // module from upstream.  Typically, this routine is a big case statement
  273.     // that dispatches to our various message handling routines.
  274.     //
  275.     // Environment: standard STREAMS entry point
  276. {
  277.     PerStreamDataPtr streamData;
  278.     T_conn_req *connReq;
  279.     OTAddress *otAddr;
  280.     InetAddress *inetAddr;
  281.     
  282.     OTAssert("VMSifyWritePut: Not the write queue", IsWriteQ(q) );
  283.  
  284.     // OTDebugBreak("VMSifyWritePut: Entered");
  285.     
  286.     streamData = GetPerStreamData(q);
  287.  
  288.     switch ( GetPrimitive(mp) ) {
  289.         case T_CONN_REQ:
  290.             OTAssert("VMSifyWritePut: We got connection request but we still think we're connected", streamData->lastConnReqPort == 0);
  291.             OTAssert("VMSifyWritePut: We got connection request but we still think we're VMSifying", !streamData->vmsify);
  292.             
  293.             connReq = (T_conn_req *) mp->b_rptr;
  294.             otAddr = (OTAddress *) (mp->b_rptr + connReq->DEST_offset);
  295.             switch (otAddr->fAddressType) {
  296.                 case AF_DNS:
  297.                     streamData->lastConnReqPort = ExtractPortFromDNSAddress( (DNSAddress *) otAddr, connReq->DEST_length);
  298.                     break;
  299.                 case AF_INET:
  300.                     OTAssert("VMSifyWritePut: Client passed bad address length, we're may make a bad decision, but TCP/IP will probably barf anyway", connReq->DEST_length == sizeof(InetAddress));
  301.                     inetAddr = (InetAddress *) otAddr;
  302.                     streamData->lastConnReqPort = inetAddr->fPort;
  303.                     break;
  304.                 default:
  305.                     OTDebugBreak("VMSifyWritePut: Bogus address format");
  306.                     streamData->lastConnReqPort = 0;
  307.                     break;
  308.             }
  309.             putnext(q, mp);
  310.             break;
  311.         default:
  312.             putnext(q, mp);
  313.             break;
  314.     }
  315.     
  316.     return 0;
  317. }
  318.  
  319. /////////////////////////////////////////////////////////////////////
  320. // Read-side put routine
  321.  
  322. static void VMSifyMessageChain(mblk_t **destPtr)
  323.     // Given a pointer to a pointer to a chain of messages
  324.     // (destPtr), replace the chain of messages with a
  325.     // chain of buffers where the data has been uppercased.
  326.     // This might involve copying the messages if the data
  327.     // they reference is read only, hence we pass in the
  328.     // address of the head of the chain, not the head
  329.     // of the chain itself.
  330. {
  331.     mblk_t *source;
  332.  
  333.     OTAssert("VMSifyMessageChain: Bad dest pointer", destPtr != nil);
  334.  
  335.     source = *destPtr;
  336.     
  337.     while ( source != nil ) {
  338.         mblk_t *next;
  339.         mblk_t *tmp;
  340.  
  341.         OTAssert("VMSifyReadPut: Oh dear", *destPtr != nil);
  342.         
  343.         // Extract next here because we might have freed
  344.         // source by the time we get to the end of the
  345.         // routine.
  346.         
  347.         next = source->b_cont;
  348.         
  349.         // See whether the source buffer is writable.
  350.         // If it is, setup tmp so that we munge it.
  351.         // If it isn't, try to copy the buffer.  If
  352.         // that works, setup tmp so that we munge
  353.         // the copy, and free the original.  If it
  354.         // fails, leave tmp set to nil so that we
  355.         // just pass through the unmunged buffer,
  356.         // which is not necessarily the best response
  357.         // to an out of memory situation, but it
  358.         // certainly is easy to implement (-:
  359.         
  360.         if (source->b_datap->db_ref == 1) {
  361.             tmp = source;
  362.         } else {
  363.             tmp = copyb(source);
  364.             if (tmp != nil) {
  365.                 freeb(source);
  366.             }
  367.         }
  368.         
  369.         // We we can write to the buffer, upper case
  370.         // it and chain it on to the destination list.
  371.         // If we can't write to it, just chain the
  372.         // unmunged buffer on to the destination list.
  373.         
  374.         if (tmp != nil) {
  375.             UInt8 *cursor;
  376.             UInt8 ch;
  377.             
  378.             OTAssert("VMSifyMessageChain: Buffer is still not writeable", tmp->b_datap->db_ref == 1);
  379.             
  380.             for (cursor = tmp->b_rptr; cursor < tmp->b_wptr; cursor++) {
  381.                 ch = *cursor;
  382.                 if (ch >= 'a' && ch <= 'z') {
  383.                     ch -= 0x20;
  384.                     *cursor = ch;
  385.                 }
  386.             }
  387.             *destPtr = tmp;
  388.         } else {
  389.             *destPtr = source;
  390.         }
  391.         
  392.         // Move to the next buffer on both the
  393.         // source and destination chains.
  394.         
  395.         destPtr = &((*destPtr)->b_cont);
  396.         source = next;
  397.     }
  398. }
  399.  
  400. static SInt32 VMSifyReadPut(queue_t* q, mblk_t* mp)
  401.     // This routine is called by STREAMS when it has a message for our
  402.     // module from downstream.  Typically, this routine is a big case statement
  403.     // that dispatches to our various message handling routines.
  404.     //
  405.     // Environment: standard STREAMS entry point
  406. {
  407.     PerStreamDataPtr streamData;
  408.     Boolean okAck;
  409.     
  410.     OTAssert("VMSifyReadPut: Not the read queue", IsReadQ(q) );
  411.  
  412.     // OTDebugBreak("VMSifyReadPut: Entered");
  413.     
  414.     streamData = GetPerStreamData(q);
  415.  
  416.     switch ( mp->b_datap->db_type ) {
  417.         case M_DATA:
  418.             if ( streamData->vmsify ) {
  419.                 VMSifyMessageChain(&mp);
  420.             }
  421.             putnext(q, mp);
  422.             break;
  423.         default:
  424.             switch ( GetPrimitive(mp) ) {
  425.                 case T_OK_ACK:
  426.                 case T_ERROR_ACK:
  427.                     okAck = (GetPrimitive(mp) == T_OK_ACK);
  428.                     switch ( ((T_ok_ack *) mp->b_rptr)->CORRECT_prim ) {
  429.                         case T_CONN_REQ:
  430.                             if (okAck) {
  431.                                 // sta_5 + ok_ack1 -> sta_6
  432.                                 // Connection in place, let's start vmsifying if required.
  433.                                 streamData->vmsify = (streamData->lastConnReqPort == kTelnetPort);
  434.                             } else {
  435.                                 // sta_5 + error_ack -> sta_3
  436.                                 // Connection attemp failed, leave vmsify false.
  437.                                 OTAssert("VMSifyReadPut: vmsify should be false but isn't", !streamData->vmsify);
  438.                             }
  439.                             streamData->lastConnReqPort = 0;
  440.                             break;
  441.                         case T_DISCON_REQ:
  442.                             if (okAck) {
  443.                                 // sta_14 + ok_ack1 -> sta_3
  444.                                 // sta_15 + ok_ack1 -> sta_3
  445.                                 // sta_16 + ok_ack1 -> sta_3
  446.                                 // Disconnect successful, connection is gone, clear vmsify.
  447.                                 streamData->vmsify = false;
  448.                             } else {
  449.                                 // sta_14 + error_ack -> sta_9
  450.                                 // sta_15 + error_ack -> sta_10
  451.                                 // sta_16 + error_ack -> sta_11
  452.                                 // Disconnect failed, connection is still in place,
  453.                                 // so is vmsify.
  454.                             }
  455.                             break;
  456.                         default:
  457.                             break;
  458.                     } 
  459.                     putnext(q, mp);
  460.                     break;
  461.                 case T_ORDREL_IND:
  462.                     // sta_10 + ordrel_ind -> sta_3
  463.                     // sta_9  + ordrel_ind -> sta_11
  464.                     streamData->vmsify = false;
  465.                     putnext(q, mp);
  466.                     break;
  467.                 case T_DISCON_IND:
  468.                     // sta_6  + discon_ind1 -> sta_3
  469.                     // sta_9  + discon_ind1 -> sta_3
  470.                     // sta_10 + discon_ind1 -> sta_3
  471.                     // sta_11 + discon_ind1 -> sta_3
  472.                     // sta_7  + discon_ind1 -> sta_3
  473.                     // sta_7  + discon_ind1 -> sta_7 ••• This is a weird one, probably need to analyse this more. •••
  474.                     streamData->vmsify = false;
  475.                     putnext(q, mp);
  476.                     break;
  477.                 case T_DATA_IND:
  478.                     if ( streamData->vmsify ) {
  479.                         VMSifyMessageChain(&mp->b_cont);
  480.                     }
  481.                     putnext(q, mp);
  482.                     break;
  483.                 default:
  484.                     putnext(q, mp);
  485.                     break;
  486.             }
  487.             break;
  488.     }
  489.         
  490.     return 0;
  491. }
  492.  
  493. /////////////////////////////////////////////////////////////////////
  494. // Static Declaration Structures
  495.  
  496. static struct module_info gModuleInfo =  
  497. {
  498.     9992,                        // Module Number, only useful for debugging
  499.     "VMSify",                    // Name of module
  500.     0,                            // Minimum data size
  501.     INFPSZ,                        // Maximum data size
  502.     16384,                        // Hi water mark for queue
  503.     4096                        // Lo water mark for queue
  504. };
  505.  
  506. static struct qinit gReadInit = 
  507. {
  508.     VMSifyReadPut,                // Put routine for "incoming" data
  509.     nil,                        // Service routine for "incoming" data
  510.     VMSifyOpen,                    // Our open routine
  511.     VMSifyClose,                 // Our close routine
  512.     nil,                        // No admin routine
  513.     &gModuleInfo                // Our module_info
  514. };
  515.  
  516. static struct qinit gWriteInit =
  517. {
  518.     VMSifyWritePut,                // Put routine for client data
  519.     nil,                        // Service routine for client data
  520.     nil,                        // open  field only used in read-side structure
  521.     nil,                        // close field only used in read-side structure
  522.     nil,                        // admin field only used in read-side structure
  523.     &gModuleInfo                // Our module_info
  524. };
  525.  
  526. static struct streamtab theStreamTab = 
  527. {
  528.     &gReadInit,                    // Our read-side qinit structure
  529.     &gWriteInit,                // Our write-side qinit structure
  530.     0,                            // We are not a mux, so set this to nil
  531.     0                            // We are not a mux, so set this to nil
  532. };
  533.  
  534. /////////////////////////////////////////////////////////////////////
  535. // Macintosh-specific Static Structures
  536.  
  537. static struct install_info theInstallInfo =
  538. {
  539.     &theStreamTab,            // Stream Tab pointer
  540.     kOTModIsModule + kOTModUpperIsTPI + kOTModIsFilter,
  541.                             // Tell OT that we are a driver, not a module
  542.     SQLVL_MODULE,            // Synchronization level, module level for the moment
  543.     0,                        // Shared writer list buddy
  544.     0,                        // Open Transport use - always set to 0
  545.     0                        // Flag - always set to 0
  546. };
  547.  
  548. // Prototypes for the exported routines below.
  549.  
  550. extern Boolean InitStreamModule(void *portInfo);
  551. extern void TerminateStreamModule(void);
  552. extern install_info* GetOTInstallInfo();
  553.  
  554. #pragma export list InitStreamModule, TerminateStreamModule, GetOTInstallInfo
  555.  
  556. // Export entry point
  557.  
  558. extern Boolean InitStreamModule(void *portInfo)
  559.     // Initialises the module before the first stream is opened.
  560.     // Should return true if the module has started up correctly.
  561.     //
  562.     // Environment: Always called at SystemTask time.
  563. {    
  564.     #pragma unused(portInfo)
  565.     Boolean result;
  566.     
  567.     OTDebugBreak("VMSify: InitStreamModule");
  568.     
  569.     result = true;
  570.     
  571.     return result;
  572. }
  573.  
  574. extern void TerminateStreamModule(void)
  575.     // Shuts down the module after the last stream has been
  576.     // closed.
  577.     //
  578.     // Environment: Always called at SystemTask time.
  579. {
  580.     // It's an excellent idea to have the following in your code, just to make
  581.     // sure you haven't left any streams open before you quit.  In theory, OT
  582.     // should not call you until the last stream has been closed, but in practice
  583.     // this can happen if you use mi_detach to half-close a stream.
  584.     
  585.     OTAssert("TerminateStreamModule: Streams are still active", gStreamList == nil);
  586. }
  587.  
  588. extern install_info* GetOTInstallInfo()
  589.     // Return pointer to install_info to STREAMS.
  590. {
  591.     return &theInstallInfo;
  592. }
  593.